home *** CD-ROM | disk | FTP | other *** search
/ Simtel MSDOS 1995 January / Simtel - 10000 MSDOS Shareware Programs (Walnut Creek)(January 1995)(Disc 2).ISO / disc2 / eel / cxtnd.e < prev    next >
Text File  |  1989-02-09  |  25KB  |  776 lines

  1.  
  2. /************************************************************************
  3. *                                                                       *
  4. *       C-Extended Mode ver 1.0                                         *
  5. *                                                                       *
  6. *       ADDITIONS AND ENHANCEMENTS--03/24/87                            *
  7. *                                                                       *
  8. *       Author: John Kubiatowicz                                        *
  9. *       KUBITRONICS information systems                                 *
  10. *       (C) March 24, 1987                                              *
  11. *       kubitron%athena.mit.edu                                         *
  12. *                                                                       *
  13. *       Much of this material is (c) 1987, by John Kubiatowicz.         *
  14. *       The remainder is (c) 1985, Lagaru Software Ltd.                 *
  15. *                                                                       *
  16. *       This material may be used and distributed freely, baring        *
  17. *       use in products for sale or lease.  The author makes no         *
  18. *       claims of perfection, and will not be responsible for           *
  19. *       damages incurred during the use of this product.                *
  20. *                                                                       *
  21. ************************************************************************/
  22. /************************************************************************
  23. * "Epsilon", "EEL" and "Lugaru" are trademarks of Lugaru Software, Ltd. *
  24. *                                                                       *
  25. *     Copyright (C) 1985 Lugaru Software Ltd.  All rights reserved.     *
  26. *                                                                       *
  27. * Limited permission is hereby granted to reproduce and modify this     *
  28. * copyrighted material provided that the resulting code is used only in *
  29. * conjunction with Lugaru products and that this notice is retained in  *
  30. * any such reproduction or modification.                                *
  31. ************************************************************************/
  32.  
  33. #include "eel.h"
  34.  
  35. /*              Original Lagaru Comments
  36.  
  37. Automatic indentation for C and EEL code.  Several indenting styles
  38. are supported, selected by the following variables.
  39.  
  40.             *   *   *   *
  41.  
  42. The position of the closing brace is controlled by the Closeback
  43. variable, 1 by default:
  44.  
  45. Closeback = 0;          Closeback = 1;
  46. if (foo) {              if (foo) {
  47.     bar();                  bar();
  48.     baz();                  baz();
  49.     }                   }
  50.  
  51. By placing the opening brace on the following line, these styles
  52. may be also used:
  53.  
  54. Closeback = 0;          Closeback = 1;
  55. if (foo)                if (foo)
  56.     {                   {
  57.     bar();                  bar();
  58.     baz();                  baz();
  59.     }                   }
  60.  
  61.  
  62.             *   *   *   *
  63.  
  64. The Topindent variable controls whether top-level statements in a function
  65. are indented.  It is 1 by default.
  66.  
  67. Topindent = 0;              Topindent = 1;
  68. foo()                       foo()
  69. {                           {
  70. if (bar)                        if (bar)
  71.     baz();                      baz();
  72. }                           }
  73.  
  74.             *   *   *   *
  75.  
  76. The Matchdelim variable controls whether typing ), ], or } displays the
  77. corresponding (, [, or { when the former is typed.  It uses the
  78. show-matching-delimiter command.
  79. */
  80.  
  81.  
  82. /*              Comments on C-Extended Mode
  83.                 John Kubiatowicz
  84.  
  85. In this file, I have implemented 'switch' statement indentation.
  86. This feature is selected by setting the flag 'Switch_form', which
  87. appears below.  The following switch format is supported:
  88.  
  89.         Closeback = 0;              Closeback = 1;
  90.     
  91.         switch(foo) {               switch(foo) {
  92.             case 0:                     case 0:
  93.                 statement1;                 statement1;
  94.                 statement2;                 statement2;
  95.             case 1:                     case 1;
  96.             case 2:                     case 2;
  97.                 statement3;                 statement3;
  98.                 statement4;                 statement4;
  99.             }                       }
  100.             
  101. In addition, I have altered the c_compute_indent() so that if
  102. the point is inside of a comment sequence, indentation
  103. will match that of the previous, non-blank line.  If the
  104. initial character on the line is a '}' and Match_delim is set,
  105. then tabbing is adjusted as in c_close, which includes showing
  106. the matching delimiter.
  107.  
  108. I have altered the c_close routine so that closing braces are aligned
  109. "properly" with opening braces.  Further, the routine
  110. 'show_matching_delimiter' has been replaced with 'c_look_for_matching'.
  111. During an attempt to match a delimiter from outside of quotes, 
  112. delimiters within single quotes are ignored, as are delimiters within
  113. double quotes.  During an attempt to match a delimiter from inside
  114. quotes, only delimiters within the same string are considered.
  115.  
  116. Ten extended marks have been added, bound to the Alt-F[1-10]
  117. and the Ctrl-F[1-10] characters.  CTRL-F[1-10] sets a mark at the 
  118. current location.  Ctrl-F[1-10] goes to the preset location.
  119. During such a move, the last position is saved and can be recalled
  120. with Alt-ESC.  Bindings for these functions can be changed, although
  121. the ten set keys must have consecutive bindings, as must the ten
  122. recall keys.  See the macros GET_MARK_BASE, SET_MARK_BASE,
  123. and C_RETRIEVE_MARK.
  124.  
  125. For those of you who don't place frequent comments in your code,
  126. a slight speed improvement in delimiter matching can be accomplished
  127. by setting the flag 'Comment_check_enable' to zero.  Comments will no
  128. longer be recognized.
  129.  
  130. The tab size automatically defaults to 4 for all .c,.h,.e, and .y
  131. files.
  132.  
  133. Finally, I have included the "reload file" function, which reloads
  134. the current file off the disk, discarding the current buffer.  This
  135. is bound to 'C-X C-R'.  Note that this command is modelled afted a
  136. similar command by Bob Knight.
  137.  
  138. I have other ideas for this code, like a new 'c_compute_indent',
  139. which I would like to implement at some later time.  Perhaps I 
  140. will work on version 1.5.....
  141.  
  142.     Good Luck,
  143.         KUBI
  144.         kubitron%athena.mit.edu
  145.         
  146. P.S. It is quite possible that this code still has some bugs in it.
  147. I would appreciate any comments or suggestions.
  148.  
  149. P.P.S. I have removed all tabs from this file.  The file was originally
  150. composed with a tab size of 4 (which is the default in c_mode).  You
  151. should probably decide on a tab size, then perform a 'tabify-region'
  152. to save space.
  153.  
  154. *****************************************************************/
  155.  
  156. /* Base for get_extended_mark and set_extended_mark keys. */
  157.  
  158. #define GET_MARK_BASE FALT(1)
  159. #define SET_MARK_BASE FCTRL(1)
  160. #define C_RETRIEVE_MARK ALT(ESC)
  161.  
  162. /*  Special global format flags */
  163.  
  164. int Switch_form = 1;        /* Switch statement flag, set to on */
  165. int Comment_check_enable = 1;
  166.  
  167. /*  C-EXT mode globals */
  168.  
  169. int Match_position = -1;    /* Indicates position of last matched delimiter.*/
  170. int quote_value = 0;        /* Indicates value of last quote match */
  171. int quote_position = -1;    /* Indicates position of last quote */
  172. int comment_value = 0;      /* Indicates value of last comment match */
  173. int comment_position = -1;  /* Indicates position of beginning of
  174.                                last comment match.  */
  175. /*  C-EXT mode buffer vars */
  176.  
  177. buffer int *extended_mark[10] = {0,0,0,0,0,0,0,0,0,0};
  178.                                 /* Ten extended marks (spots start at NULL) */
  179. buffer int *prev_ext_mark = 0;  /*  Previous extended mark */
  180.                                 
  181. keytable c_tab;         /* key table for c mode */
  182.  
  183. /* by default, the indenting levels are one tab stop apart */
  184. #define INCR    tab_size
  185.  
  186. /* define an RE matching C comments or whitespace */
  187. #define C_SKIP "((/%*([^*]|[^/]%*)*%*/)|[ \t\n])*"
  188.  
  189. c_indenter()        /* replace surrounding indentation with new */
  190. {
  191.     to_column(c_compute_indent());
  192. }
  193.  
  194. command c_look_for_matching();
  195. command c_set_extended_mark();
  196. command c_get_extended_mark();
  197. command c_retrieve_mark();
  198.         
  199. #define SMB(c) (SET_MARK_BASE + (c))
  200. #define GMB(c) (GET_MARK_BASE + (c))
  201.  
  202. command c_mode()
  203. {
  204.     mode_keys = c_tab;      /* use these keys */
  205.     if (Matchdelim)
  206.         c_tab[')'] = c_tab[']'] = (short) c_look_for_matching;
  207.     c_tab[SMB(0)] = c_tab[SMB(1)] = (short) c_set_extended_mark;
  208.     c_tab[SMB(2)] = c_tab[SMB(3)] = (short) c_set_extended_mark;
  209.     c_tab[SMB(4)] = c_tab[SMB(5)] = (short) c_set_extended_mark;
  210.     c_tab[SMB(6)] = c_tab[SMB(7)] = (short) c_set_extended_mark;
  211.     c_tab[SMB(8)] = c_tab[SMB(9)] = (short) c_set_extended_mark;
  212.     c_tab[GMB(0)] = c_tab[GMB(1)] = (short) c_get_extended_mark;
  213.     c_tab[GMB(2)] = c_tab[GMB(3)] = (short) c_get_extended_mark;
  214.     c_tab[GMB(4)] = c_tab[GMB(5)] = (short) c_get_extended_mark;
  215.     c_tab[GMB(6)] = c_tab[GMB(7)] = (short) c_get_extended_mark;
  216.     c_tab[GMB(8)] = c_tab[GMB(9)] = (short) c_get_extended_mark;
  217.     c_tab[C_RETRIEVE_MARK] = (short) c_retrieve_mark;
  218.     major_mode = strsave("C-Extended");
  219.     make_mode();
  220.     indenter = c_indenter;
  221.     auto_indent = 1;
  222.     tab_size = 4;
  223. }
  224.  
  225. /* make this the default mode for .c, .h, .e, and .y files */
  226. suffix_c()  { c_mode(); }
  227. suffix_h()  { c_mode(); }
  228. suffix_e()  { c_mode(); }
  229. suffix_y()  { c_mode(); }
  230.  
  231. /*  Look at the last two lines and return the correct indentation
  232.     for the current line, assuming C source (or similar).  At start,
  233.     we must be at the end of a line's indentation.
  234. */
  235.  
  236. c_compute_indent()
  237. {
  238.     int orig = point, prev_keyword;
  239.     int prev_end = ';', prev2_end = ';';    /* last chr on 2 prev lines */
  240.     int ind = 0;                /* indentation to use */
  241.     int first_char;             /* cur line's first char */
  242.  
  243.     /* If within a comment, match indentation to previous line */
  244.     
  245.     if (in_comment_check()) {
  246.         re_search(-1, "[ \t\n]*");
  247.         to_indentation();
  248.         ind = current_column(); /* Current column should match previous */
  249.         point = orig;
  250.         return ind;
  251.     }
  252.     first_char = curchar();
  253.     if (first_char == '}' && Matchdelim) {
  254.         point++;
  255.         c_close_compute();
  256.         point--;
  257.         return current_column();
  258.     }           
  259.     to_begin_line();
  260.     re_search(-1, C_SKIP);      /* skip whitespace and comments */
  261.     if (point > 0) {
  262.         prev_end = character(--point);  /* get last relevant char */
  263.         to_indentation();
  264.         ind = current_column(); /* and indentation of its line */
  265.         prev_keyword = point;   /* save its position too */
  266.  
  267.         re_search(-1, C_SKIP);  /* skip whitespace and comments */
  268.         if (point > 0)      /* and get last interesting char on */
  269.             prev2_end = character(point-1); /* line before that */
  270.  
  271.         point = prev_keyword;   /* prepare for checking prev line */
  272.     }
  273.  
  274.     if (!ind) { /* special case if no previous indentation */
  275.         if (top_indent(prev_end, prev2_end))
  276.             ind += INCR;
  277.     } else if (prev_end == ';') {
  278.         if (!index(";{}", prev2_end) && (!Switch_form || prev2_end != ':'))
  279.             ind -= INCR;
  280.     } else if (prev_end == '}') {
  281.         if (!Closeback)
  282.             ind -= INCR;
  283.     } else if (prev_end != '{' || index(";{}", prev2_end)
  284.             || c_statement_start() || just_open())
  285.         ind += INCR;
  286.     /* otherwise, last is probably continuation of a stmt or func decl */
  287.     if (Closeback && first_char == '}')
  288.         ind -= INCR;
  289.     point = orig;
  290.     return ind;
  291. }
  292.  
  293. /*  Look at the last two lines and guess at the correct indentation,
  294.     assuming C source (or similar).  If we're not in this line's
  295.     indentation, though, or our new indentation matches the old,
  296.     just insert a tab.
  297. */
  298.  
  299. c_indent() on c_tab['\t']
  300. {
  301.     int orig = point;
  302.     int orig_column = current_column();
  303.  
  304.     to_indentation();
  305.     if (orig_column > current_column()) {   /* if not in indentation */
  306.         point = orig;
  307.         insert('\t');           /* insert a tab */
  308.     } else if (prev_cmd == C_INDENT)    /* repeated, make bigger */
  309.         to_column(orig_column + INCR);
  310.     else
  311.         to_column(c_compute_indent());
  312.     this_cmd = C_INDENT;
  313. }
  314.  
  315. /*
  316. Tell if more indent is required.
  317. Assumes last line had no indent and point is at its start.
  318. The hard case is distinguishing between these when Topindent is zero:
  319.  
  320.     foo();              foo();
  321.     }                   }
  322.     func(bar)           if (bar)
  323.  
  324. When point is just after these examples, we must examine the word
  325. at "func" to distinguish the left example (the end of a function and
  326. beginning of the next, requiring no indentation) from the right (the
  327. end of a block and start of a conditional, requiring indentation).
  328. */
  329.  
  330. top_indent(prev_end, prev2_end)
  331. {
  332. int ret;
  333.  
  334. if (Topindent)
  335.     ret = prev_end == '{';
  336. else if (ret = !index(";}", prev_end) && index(";{}", prev2_end))
  337.     if (prev_end != '{' && !c_statement_start())
  338.         ret = 0;
  339. return ret;
  340. }
  341.  
  342. /*
  343. Tell if the last line begins a statement (rather than a function
  344. declaration or the continuation of a previous statement).
  345. Assumes point is at the start of the last line.
  346. */
  347. c_statement_start()
  348. {
  349. return parse_string(1, "[{} \t]*(if|else|while|do|for)[^a-zA-Z0-9]",
  350.     (char *) 0);
  351. }
  352.  
  353. just_open()
  354. {
  355. return Closeback && parse_string(1, "{[ \t]*\n", (char *) 0);
  356. }
  357.  
  358. /*  fix indentation if necessary when { is typed */
  359. c_open() on c_tab['{']
  360. {
  361.     int orig = point;
  362.     int orig_column;        /* point's column */
  363.     int ind;
  364.  
  365.     if (Closeback) {
  366.         orig_column = current_column();
  367.         to_indentation();
  368.         ind = current_column();     /* get line's indentation */
  369.         point = orig;
  370.         if (orig_column <= ind)     /* if in indentation */
  371.             to_column(ind - INCR);
  372.     }
  373.     normal_character();
  374. }
  375.  
  376. /*  fix indentation if necessary when } is typed */
  377. c_close() on c_tab['}']
  378. {
  379.     if (point && check_single()) {
  380.         normal_character();
  381.         return;
  382.     } else {
  383.         normal_character();
  384.         c_close_compute();
  385.     }
  386. }
  387.  
  388. c_close_compute()
  389. {
  390.     int orig;
  391.     int orig_column,p_orig_column;      /* point's column */
  392.     int quoflg,comflg,abspos;
  393.     int temp,ind;
  394.     char first,second;
  395.     char *left = "[{(", *right = "]})";
  396.  
  397.     orig = point;
  398.     orig_column = current_column() - 1;
  399.     to_indentation();
  400.     ind = current_column();
  401.     if (ind < orig_column) {
  402.         point = orig;
  403.         return;
  404.     }
  405.     if (Matchdelim) {
  406.         point = orig;
  407.         match_character();
  408.         abspos = -1;
  409.         if (comflg = in_comment_check())
  410.             abspos = comment_position;
  411.         if (Match_position >= 0) {
  412.             point = p_orig_column = Match_position;
  413.             while (re_search(-1,"[]})\n]") && curchar() != '\n'
  414.                     && point > abspos)
  415.                 if (comflg == in_comment()) {
  416.                     point++;
  417.                     second = character(point - 1);
  418.                     first = left[index(right,second)-right];
  419.                     if (!search_close(first,second)) {
  420.                         say("Unmatched delimiter -- %c",second);
  421.                         break;  
  422.                     } else
  423.                         point = Match_position;
  424.                 }
  425.             if (curchar() != '\n') 
  426.                 point = p_orig_column;
  427.             else
  428.                 point++;
  429.             to_indentation();
  430.             ind = current_column();
  431.             point = orig - 1;
  432.             to_column(ind);
  433.             point++;
  434.             return;
  435.         }
  436.     } else if (Closeback) {
  437.         if (orig_column <= ind)     /* if in indentation */
  438.             to_column(ind - INCR);
  439.         point = orig;
  440.     }
  441. }
  442.  
  443. /*  This routine implements indentation of switch statements.
  444.     When the ':' character is entered, it checks first to make sure
  445.     that the colon is not imbedded in some comment.  Then, it makes
  446.     sure that it is not imbedded in a string.  Finally, if the previous
  447.     line does not end in '{', the whole line is back indented on tab space.
  448.         
  449.         Author: John Kubiatowicz
  450.         kubitron@athena.mit.edu
  451. */
  452.  
  453. switch_back() on c_tab[':']
  454. {
  455.     int ind;
  456.     int orig;
  457.     
  458.     normal_character();
  459.     orig = point;
  460.     point--;
  461.     if (!Switch_form || in_comment_check() ||
  462.             in_quote_check() || char_exists('?') || check_single()) {
  463.         point++;
  464.         return; /*  Do nothing special  */
  465.     }
  466.     to_indentation();
  467.     ind = current_column();
  468.     re_search(-1,C_SKIP);
  469.     if (point > 0 && index(":};",character(--point)))
  470.         ind -= INCR;
  471.     point = orig;
  472.     to_indentation();
  473.     to_column(ind);
  474.     to_end_line();
  475.     search(-1,":");
  476.     point++;
  477. }
  478.  
  479.  
  480. /*  This checks to see if the point is within a comment. */
  481.  
  482. #define COM_SEARCH "(/%*)|(%*/)"
  483.  
  484. int in_comment_check()
  485. {
  486.     comment_position = -1;
  487.     return in_comment();
  488. }
  489.  
  490. int in_comment()
  491. {   
  492.     int orig = point;
  493.  
  494.     if (!Comment_check_enable)
  495.         return 0;
  496.     if (comment_position != -1 && point > comment_position)
  497.         return comment_value;
  498.     quote_position = -1;
  499.     while (re_search(-1,COM_SEARCH) && curchar() == '/' && in_quotes());
  500.     comment_position = point;   /* Point at which answer was derived */
  501.     comment_value = parse_string(1,"/*",(char *)0);
  502.     point = orig;
  503.     return comment_value;
  504. }
  505.  
  506. /*  This routine checks to see if the current character is imbedded
  507.     within a set of quotes. It returns 'TRUE' if the character follows
  508.     an odd number of double quotes on the current line. */
  509.  
  510. int in_quote_check()
  511. {
  512.     quote_position = -1;    /* Force full scan */
  513.     return in_quotes();
  514. }
  515.  
  516. /*  This routine checks the variable 'quote_position.'  If it is not
  517.     equal to -1, and point is greater than it, it returns the value
  518.     'quote_value.'  Otherwise, it scans backward from the current
  519.     point to see if there are an odd number of points between the 
  520.     current point and the beginning of the line.
  521. */
  522.  
  523. int in_quotes()
  524. {
  525.     int orig = point;
  526.  
  527.     if (quote_position != -1 && point > quote_position)
  528.         return quote_value; /*  Previous result still valid */
  529.     quote_position = -1;
  530.     quote_value = 0;        
  531.     while ((--point) >= 0 && curchar() != '\n')
  532.         if (curchar() == '"' &&
  533.             !(point > 0 && ((character(point - 1) == '\'' &&
  534.                                  character(point + 1) == '\'') ||
  535.                                 escseq(point)))) {
  536.             quote_value = !quote_value; /* Toggle the quote flag */
  537.             if (quote_position == -1) 
  538.                 quote_position = point;
  539.         }
  540.     if (quote_position == -1)
  541.         quote_position = point;
  542.     point = orig;
  543.     return (quote_value);
  544. }
  545.  
  546. /*  Check to see if the character at 'check_point' is part of 
  547.     an escape sequence.
  548. */
  549.  
  550. escseq(check_point)
  551. char check_point;
  552. {   
  553.     int returnval = 0;
  554.     
  555.     while (--check_point > 0 && character(check_point) == '\\')
  556.         returnval = !returnval;
  557.     return returnval;
  558. }   
  559.     
  560. /*  Check to see if the character at 'point' could potentially
  561.     be enclosed in single quotes or part of an escape sequence ('\').
  562.     Note that an ending single quote is not required.  This routine
  563.     is used as characters are being typed in, so presumably the final
  564.     quote doesn't exist yet.
  565. */
  566.  
  567. int check_single()
  568. {
  569.     int backcount = 0;
  570.     int check_point = point;
  571.     
  572.     if (!check_point)
  573.         return 0;
  574.     if (character(check_point - 1) == '\'' && !in_quotes()) {
  575.         if (check_point == 1)
  576.             return 1;   /*  Point of interest in quotes */
  577.         if (character(check_point - 2) == '\\' &&
  578.                 (check_point == 2 || character(check_point - 3) != '\\'))
  579.             return 0;   /*  Single quote is part of escape sequence */
  580.         return (check_point == 2 || 
  581.                 (character(check_point - 3) != '\'' &&
  582.                     (check_point == 3 || character(check_point - 3) != '\\'
  583.                         || character(check_point - 4) != '\'')));
  584.     } else if (character(check_point - 1) == '\\')
  585.         return escseq(check_point);
  586.     else 
  587.         return 0;
  588. }
  589.  
  590. /*  Check to see if the specified character exists earlier in the line. */
  591.  
  592. int char_exists(desired)
  593. char desired;
  594. {
  595.     int orig = point;
  596.     
  597.     while ((--point) >= 0 && curchar() != '\n')
  598.         if (curchar() == desired) {
  599.             point = orig;
  600.             return 1;   /* char exists */
  601.         }
  602.     point = orig;
  603.     return 0;   /*  Char does not exist */
  604. }
  605.  
  606. /* Search back for matching closing delimiter */
  607.  
  608. int search_close(lchar,rchar)
  609. char lchar,rchar;
  610. {
  611.     char searchstr[20];
  612.     jmp_buf *old_level = top_level, this_level;
  613.     int orig = point;
  614.     int extradel = 0,quoflg,comflg,abspos;
  615.     
  616.     Match_position = -1;    /* No position to match */
  617.     top_level = &this_level;
  618.     if (setjmp(top_level)) {
  619.         top_level = old_level;
  620.         point = orig;
  621.         return 0;
  622.     }
  623.     if (rchar == ']')
  624.         strcpy(searchstr,"[][]");
  625.     else
  626.         sprintf(searchstr,"[%c%c]",lchar,rchar);
  627.     abspos = -1;
  628.     if (comflg = in_comment_check())
  629.         abspos = comment_position;
  630.     if (quoflg = in_quote_check() && quote_position > abspos)
  631.         abspos = quote_position;
  632.     while (re_search(-1,searchstr) && point > abspos)
  633.         if (comflg == in_comment() && quoflg == in_quotes()
  634.                 && !check_single())
  635.             if (curchar() == rchar)
  636.                 extradel++;
  637.             else {
  638.                 extradel--;
  639.                 if (!extradel)
  640.                     break;
  641.             }
  642.     top_level = old_level;
  643.     if (curchar() == lchar && !extradel) {
  644.         Match_position = point;
  645.         point = orig;
  646.         return 1;
  647.     } else {
  648.         point = orig;
  649.         return 0;
  650.     }
  651. }
  652.  
  653. #define LEFTD   "[({"
  654. #define RIGHTD  "])}"
  655.  
  656. /*  This routine attempts to search for a matching delimiter,
  657.     then briefly shows it on the screen. */
  658.  
  659. command c_look_for_matching()
  660. {
  661.     normal_character();
  662.     point--;
  663.     if (!check_single()) {
  664.         point++;
  665.         match_character();
  666.     } else
  667.         point++;
  668. }
  669.  
  670. match_character()
  671. {   
  672.     char first,second;
  673.     char *left = LEFTD, *right = RIGHTD,*foo;
  674.     
  675.     say("");
  676.     second = character(point - 1);
  677.     if (foo = index(right,second)) {
  678.         first = left[foo - right];
  679.         if (search_close(first,second))
  680.             show_match_line();
  681.         else
  682.             say("Unmatched delimiter");
  683.     }
  684.  
  685. /*  Briefly display the window at Match_position, then return to point */
  686.  
  687. show_match_line()
  688. {
  689.     int orig = point;
  690.     int time, oldstart = window_start;
  691.     
  692.     point = Match_position;
  693.     maybe_refresh();
  694.     time = (window_start == oldstart) ? 5 : 10;
  695.     pause(time);
  696.     window_start = oldstart;
  697.     point = orig;
  698.     build_first = 1;
  699. }
  700.  
  701. /*  The following commands implement the extended mark functions.
  702.     Their names are basically self explanatory. 
  703. */
  704.  
  705. command c_set_extended_mark()
  706. {
  707.     int fnum = key - SET_MARK_BASE;
  708.     
  709.     if (fnum < 0 || fnum > 9)
  710.         error("Bad key-- c_set_extended_mark");
  711.     if (extended_mark[fnum] == NULL)
  712.         extended_mark[fnum] = alloc_spot();
  713.     *(extended_mark[fnum]) = point; /* Get sticky mark */
  714.     say("Extended mark #%d set.",fnum + 1);
  715. }
  716.  
  717. command c_get_extended_mark()
  718. {
  719.     int fnum = key - GET_MARK_BASE;
  720.     
  721.     if (fnum < 0 || fnum > 9)
  722.         error("Bad key-- c_get_extended_mark");
  723.     if (extended_mark[fnum] == NULL)
  724.         error("Extended mark #%d not set.",fnum+1);
  725.     else {
  726.         if (prev_ext_mark == NULL)
  727.             *(prev_ext_mark = alloc_spot()) = 0;
  728.         *prev_ext_mark = point;
  729.         point = *(extended_mark[fnum]);
  730.         build_first = 1;
  731.         say("Previous position saved.");
  732.     }
  733. }
  734.  
  735. command c_retrieve_mark()
  736. {
  737.     int temp = point;
  738.     
  739.     if (prev_ext_mark == NULL)
  740.         error("No previous position.");
  741.     else {
  742.         point = *prev_ext_mark;
  743.         *prev_ext_mark = temp;
  744.         build_first = 1;
  745.         say("Previous position saved.");
  746.     }
  747. }
  748.  
  749. /*  Restore the current buffer from its associated disk file.
  750.     Note that two types of buffers cannot be reverted, process
  751.     buffers, and directory buffers.  Note that the extended marks
  752.     are returned to their uninitialized states and the pointer
  753.     is placed at the beginning of the buffer.
  754. */
  755.  
  756. command reload_file() on cx_tab[CTRL('R')]
  757. {
  758.     char answer[80];
  759.     int count;
  760.     
  761.     if(!strcmp(bufname,"process"))
  762.         error("Process buffers cannot be reloaded!");
  763.     else if (!strcmp(bufname,"dired"))
  764.         error("Directory buffers cannot be reloaded!");
  765.     else {
  766.         get_string(answer, "Restore file from disk? [n]");
  767.         if (toupper(*answer) == 'Y') {
  768.             read_file(filename);
  769.             for (count = 0; count < 10; count++)
  770.                 extended_mark[count] = NULL;
  771.             prev_ext_mark = NULL;
  772.         }
  773.     }
  774. }
  775.